﻿using System;
using System.Drawing;
using System.IO;
using System.Linq;

using We.Moonlight;
using We.Moonlight.Geometry;
using We.Moonlight.MSql;

namespace We.Moonlight.ViItem
{
    public class ViDoc : IVi
    {
        public static ViDoc Sample;

        #region Constructors

        public ViDoc(mDB.DocumentsRow DR)
        {
            id = DR.ID;
            DeskID = DR.DeskID;
            try {
                File = new FileInfo(DR.Path);
                _FileType = getType(DR.Path);
                fBmp = getPreview(DR.Path);
            } 
            catch (Exception) 
            {
                File = null;
                _FileType = ViFileType.Document;
                fBmp = Properties.Resources.EmptyViDoc;
            }

            fLoc = new Point(DR.LocX, DR.LocY);
            fRot = DR.Rot;
            fScl = DR.Scl;
            Flip(DR.Flipped.ToBool());
            Layer = DR.Layer;
            InitializeGeometry();
            Name = DR.Name;
            _ViType = (ViType)DR.Type;
        }
        public ViDoc(Point PT)
        {
            fBmp = Properties.Resources.EmptyViDoc;
            fLoc = PT;
            InitializeGeometry();
        }

        //private void Initialize(Bitmap B, Point Loc, int Layer)
        //{
        //    //Check the Parameters for validness
        //    if (B == null) throw new ArgumentNullException("Cannot Use an Empty Bitmap for a Visual Document.");
        //    if (B.Width == 0 || B.Height == 0) throw new Exception("The Bitmap for a Visual Document cannot be 0 pixels high/wide.");
        //    //Assign some Values
        //    fBmp = B;
        //    fLoc = Loc;
        //    layer = Layer;
        //    InitializeGeometry();
        //}
        private void InitializeGeometry()
        {
            //Calculate Geometry Values. Due to flipped coordinate Systems, angles increase Clockwise!
            //For Explanation: linear Equation f(x) = 0*x + My where My is the Y-Coordinate of The Center Point
            standardc = (Math.Sqrt(fBmp.Width * fBmp.Width + fBmp.Height * fBmp.Height) / 2);//Distance from Center Point to one Corner
            if (_FileType == ViFileType.Image)
            {
                standardc += Math.Sqrt(288);
            }
            BMX = Math.PI - Math.Atan(fBmp.Height / fBmp.Width);                             //Angle Between Left-Bottom Corner, Middle Point, X (Point on f where Xx > Mx (where Xx and Mx are the X-Coordinates of X and M, resp.))
            CMX = Math.PI + Math.Atan(fBmp.Height / fBmp.Width);                             //Angle Between Left-Top Corner, Middle Point, X
            TMX = 0 - Math.Atan(fBmp.Height / fBmp.Width);                                   //Angle Between Right-Top Corner, Middle Point, X
            RMX = 0 + Math.Atan(fBmp.Height / fBmp.Width);                                   //Angle Between Right-Bottom Corner, Middle Point, X
            _Corners = Cornerpoints();
        }

        #endregion
        #region Events
        public delegate void OnFlipHandler(object sender, EventArgs e);
        public event OnFlipHandler OnFlip;
        #endregion
        #region Fields

        double TMX; //Standard* <C(Top    Left ) M X
        double CMX; //Standard* <T(Top    Right) M X
        double BMX; //Standard* <B(Bottom Left ) M X
        double RMX; //Standard* <R(Bottom Right) M X
        //Standard: Drehung = 0
        double standardc; //strecke M-C
        //Mittelpunkt zum Malen
        Point fLoc;
        //Drehwinkel
        double fRot = 0.0d;
        //Skalierungsfaktor
        double fScl = 1.0d;
        //Bitmap die gemalt werden soll (Hier 200px * 200px Schwarz)
        Bitmap fBmp;
        //an Integer to Decide the Z-Order of the ViDocs. Higher layer values mean the Control is higher above others.
        int layer;
        //File informaton
        private FileInfo file;
        //showing notes?
        bool flipped;
        //ID of the ViDoc
        private int id;
        //ID the containing desk
        private int deskid;
        //name, not jet implemented
        private string _name;
        //document/Process/Executable, Album or others, so one can tell what it is when he has only an IVi object
        private ViType _ViType;
        //Image or not, for the border
        private ViFileType _FileType;
        //the Angle of flipping, clockwise when viewed from top
        private int flipAngle;
        //the Points; they're updated whenever need be
        private Point[] _Corners = new Point[] { new Point(0, 0), new Point(0, 0), new Point(0, 0) };

        #endregion
        #region Properties

        public override Point Location
        {
            get { return fLoc; }
            set { Translate(value.X, value.Y, true); }
        }
        public override double ScaleFactor
        {
            get { return fScl; }
            set { Scale(value, true); }
        }
        public override double Rotation
        {
            get { return fRot; }
            set { Rotate(value, true); }
        }

        public override Point[] Corners
        {
            get { return _Corners; }
        }
        public Point C
        {

            get
            {
                double c2 = standardc * fScl;  //Distance from Center Point to one Corner
                return new Point(
                        (int)(fLoc.X + (Math.Cos(0 - (CMX + Conversion.Deg2Rad(fRot))) * c2)),
                        (int)(fLoc.Y - (Math.Sin(0 - (CMX + Conversion.Deg2Rad(fRot))) * c2))
                    );
            }
        }
        public Point T
        {
            get
            {
                double c2 = standardc * fScl;  //Distance from Center Point to one Corner
                return new Point(
                        (int)(fLoc.X + (Math.Cos(0 - (TMX + Conversion.Deg2Rad(fRot))) * c2)),
                        (int)(fLoc.Y - (Math.Sin(0 - (TMX + Conversion.Deg2Rad(fRot))) * c2))
                    );
            }
        }
        public Point B
        {

            get
            {
                double c2 = standardc * fScl;  //Distance from Center Point to one Corner
                return new Point(
                        (int)(fLoc.X + (Math.Cos(0 - (BMX + Conversion.Deg2Rad(fRot))) * c2)),
                        (int)(fLoc.Y - (Math.Sin(0 - (BMX + Conversion.Deg2Rad(fRot))) * c2))
                    );
            }
        }
        public Point R
        {
            get
            {
                double c2 = standardc * fScl;  //Distance from Center Point to one Corner
                return new Point(
                    (int)(fLoc.X + (Math.Cos(0 - (RMX + Conversion.Deg2Rad(fRot))) * c2)),
                    (int)(fLoc.Y - (Math.Sin(0 - (RMX + Conversion.Deg2Rad(fRot))) * c2))
                );
            }
        }

        public override Bitmap Bmp
        {
            get 
            {
                if (flipped)
                    return getNotes("Hi" + Environment.NewLine + "User!", fBmp.Width, fBmp.Height, new Font("Consolas", 10));
                else
                    if (_FileType == ViFileType.Image)
                    {
                        Bitmap Bordered = new Bitmap(fBmp.Width + 26, fBmp.Height + 26);
                        Graphics gr = Graphics.FromImage(Bordered);
                        gr.Clear(Color.Transparent);
                        gr.FillRectangle(Brushes.White, 1, 1, fBmp.Width + 25, fBmp.Height + 25);
                        gr.DrawImage(fBmp, new Point(13, 13));
                        gr.DrawRectangle(Pens.DimGray, 1, 1, fBmp.Width + 24, fBmp.Height + 24);
                        gr.DrawRectangle(Pens.DimGray, 13, 13, fBmp.Width, fBmp.Height);
                        return Bordered;
                    }
                    else
                        return fBmp; 
            
            }
        }

        private Line[] Borderlines
        {
            get
            {
                double c2 = standardc * fScl;  //Distance from Center Point to one Corner
                return new Line[]{
                    new Line(
                        new Point(                    (int)(fLoc.X + (Math.Cos(0-(CMX + Conversion.Deg2Rad(fRot)))*c2)),                    (int)(fLoc.Y - (Math.Sin(0-(CMX + Conversion.Deg2Rad(fRot)))*c2))), 
                        new Point(                    (int)(fLoc.X + (Math.Cos(0-(TMX + Conversion.Deg2Rad(fRot)))*c2)),                    (int)(fLoc.Y - (Math.Sin(0-(TMX + Conversion.Deg2Rad(fRot)))*c2)))
                    )
                ,
                    new Line(
                        new Point(                    (int)(fLoc.X + (Math.Cos(0-(BMX + Conversion.Deg2Rad(fRot)))*c2)),                    (int)(fLoc.Y - (Math.Sin(0-(BMX + Conversion.Deg2Rad(fRot)))*c2))),
                        new Point(                    (int)(fLoc.X + (Math.Cos(0-(RMX + Conversion.Deg2Rad(fRot)))*c2)),                    (int)(fLoc.Y - (Math.Sin(0-(RMX + Conversion.Deg2Rad(fRot)))*c2)))
                    )
                ,
                    new Line(
                        new Point(                    (int)(fLoc.X + (Math.Cos(0-(CMX + Conversion.Deg2Rad(fRot)))*c2)),                    (int)(fLoc.Y - (Math.Sin(0-(CMX + Conversion.Deg2Rad(fRot)))*c2))), 
                        new Point(                    (int)(fLoc.X + (Math.Cos(0-(BMX + Conversion.Deg2Rad(fRot)))*c2)),                    (int)(fLoc.Y - (Math.Sin(0-(BMX + Conversion.Deg2Rad(fRot)))*c2)))
                    )
                ,
                    new Line(
                        new Point(                    (int)(fLoc.X + (Math.Cos(0-(TMX + Conversion.Deg2Rad(fRot)))*c2)),                    (int)(fLoc.Y - (Math.Sin(0-(TMX + Conversion.Deg2Rad(fRot)))*c2))), 
                        new Point(                    (int)(fLoc.X + (Math.Cos(0-(RMX + Conversion.Deg2Rad(fRot)))*c2)),                    (int)(fLoc.Y - (Math.Sin(0-(RMX + Conversion.Deg2Rad(fRot)))*c2)))
                    )
                };

            }
        }
        public override int Layer
        {
            get { return layer; }
            set { layer = value; }
        }
        public override bool Flipped
        {
            get { return flipped; }
            set { Flip(!value); }
        }
        public override int ID
        {
            get { return id; }
        }
        public override int DeskID
        {
            get { return deskid; }
            set { deskid = value; }
        }

        public override string Name
        {
            get { return _name; }
            set { _name = value; }
        }
        public override FileInfo File
        {
            get { return file; }
            set { file = value; }
        }
        public override ViType Type
        {
            get { return _ViType; }
        }
        public override ViFileType FileType
        {
            get
            {
                return _FileType;
            }
        }
        public int IncFlipAngle()
        {
            flipAngle++;
            return flipAngle;
        }

        #endregion
        #region Methods

        public override bool isOn(Point P)
        {
            Line LTop = new Line(C, T);
            Line LBottom = new Line(B, R);
            Line LLeft = new Line(C, B);
            Line LRight = new Line(T, R);
            return
                (
                    Line.IsBetweenOrOn(LTop, LBottom, P)
                    &&
                    Line.IsBetweenOrOn(LLeft, LRight, P)
                );
        }
        public override void Flip()
        {
            flipped = !flipped;
            if (OnFlip != null)
                OnFlip.Invoke(this, EventArgs.Empty);
        }
        public override void Flip(bool ShowNotes)
        {
            flipped = ShowNotes;
            if(OnFlip != null)
                OnFlip.Invoke(this, EventArgs.Empty);
        }
        public override void Flip(FlipSide FS)
        {
            flipped = (FS == FlipSide.Image ? false : true);
            if (OnFlip != null)
                OnFlip.Invoke(this, EventArgs.Empty);
        }
        public override void Flash(bool flashing)
        {
            throw new NotImplementedException();
        }

        public override void Clicked(System.Windows.Forms.MouseButtons MB)
        {
            switch (MB)
            {
                case System.Windows.Forms.MouseButtons.Left:
                    //execute, or for a gadget do input-redirection
                    break;
                case System.Windows.Forms.MouseButtons.Middle:
                    Flip();
                    break;
                case System.Windows.Forms.MouseButtons.Right:
                    //Open context menu for this Item
                    break;
            }
        }

        public override void Scale(double factor, bool absolute)
        {
            //Skalierungsfaktor Setzen
            if (absolute)
            {
                fScl = factor;
            }
            else
            {
                fScl *= factor;
            }
            _Corners = Cornerpoints();
        }
        public override void Translate(int x, int y, bool absolute)
        {
            //Ort setzen
            if (absolute)
            {
                fLoc = new Point(x, y);
            }
            else
            {
                fLoc = new Point(fLoc.X + x, fLoc.Y + y);
            }
            _Corners = Cornerpoints();
        }
        public override void Rotate(double angle, bool absolute)
        {
            //Drehwinkel setzen
            if (absolute)
            {
                fRot = angle;
            }
            else
            {
                fRot += angle;
            }
            _Corners = Cornerpoints();

            if (fRot >= 360.0d) fRot -= 360.0d;
            if (fRot <= -360.0d) fRot += 360.0d;
        }
        private Point[] Cornerpoints()
        {
            double c2 = standardc * fScl;  //Distance from Center Point to one Corner
            double deltaX = Math.Sin(flipAngle) * fBmp.Width / 2.0d;
            double deltaY = Math.Sin(flipAngle) * fBmp.Height / 2.0d;
            return new Point[]
            {
                //Punkt Links Oben
                new Point(
                    (int)Math.Round(
                        (fLoc.X + (Math.Cos(0-(CMX + Conversion.Deg2Rad(fRot)))*c2)) + deltaX
                            , 0),
                    (int)Math.Round(
                        (fLoc.Y - (Math.Sin(0-(CMX + Conversion.Deg2Rad(fRot)))*c2)) + deltaY
                            , 0)
                ), 
                //Punkt Rechts Oben
                new Point(
                    (int)Math.Round(
                        (fLoc.X + (Math.Cos(0-(TMX + Conversion.Deg2Rad(fRot)))*c2)) - deltaX
                            , 0),
                    (int)Math.Round(
                        (fLoc.Y - (Math.Sin(0-(TMX + Conversion.Deg2Rad(fRot)))*c2)) - deltaY
                            , 0)
                ), 
                //Punkt Links Unten
                new Point(
                    (int)Math.Round(
                        (fLoc.X + (Math.Cos(0-(BMX + Conversion.Deg2Rad(fRot)))*c2)) + deltaX
                            , 0),
                    (int)Math.Round(
                        (fLoc.Y - (Math.Sin(0-(BMX + Conversion.Deg2Rad(fRot)))*c2)) - deltaY
                            , 0)
                )};
        }
        
        private Bitmap getNotes(string lines, int width, int height, Font F)
        {
            return IVi.getLines(lines, new Size((int)(width * fScl), (int)(height * fScl)), Color.Beige, new Font(F.Name, (float)12 * (float)fScl), (int)(12 * fScl), (int)(5 * fScl), (int)(12 * fScl));
        }

        #region Database Methods
        public override void Insert()
        {
            SqlViItem.Insert(this, this.DeskID);
        }
        public override void Update()
        {
            SqlViItem.Update(this);
        }
        public override void Delete()
        {
            SqlViItem.Delete(this);
        }
        #endregion

        #endregion       
        

        
    }
}